home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / system-config-printer / troubleshoot / PrintTestPage.py < prev    next >
Encoding:
Python Source  |  2009-05-05  |  19.0 KB  |  498 lines

  1. #!/usr/bin/env python
  2.  
  3. ## Printing troubleshooter
  4.  
  5. ## Copyright (C) 2008, 2009 Red Hat, Inc.
  6. ## Copyright (C) 2008, 2009 Tim Waugh <twaugh@redhat.com>
  7.  
  8. ## This program is free software; you can redistribute it and/or modify
  9. ## it under the terms of the GNU General Public License as published by
  10. ## the Free Software Foundation; either version 2 of the License, or
  11. ## (at your option) any later version.
  12.  
  13. ## This program is distributed in the hope that it will be useful,
  14. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. ## GNU General Public License for more details.
  17.  
  18. ## You should have received a copy of the GNU General Public License
  19. ## along with this program; if not, write to the Free Software
  20. ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. import cups
  23. import dbus
  24. import dbus.glib
  25. import gobject
  26. import os
  27. import pango
  28. import tempfile
  29. import time
  30. from timedops import TimedOperation
  31.  
  32. from base import *
  33.  
  34. import errordialogs
  35. errordialogs.set_gettext_function (_)
  36. from errordialogs import *
  37.  
  38. DBUS_PATH="/com/redhat/PrinterSpooler"
  39. DBUS_IFACE="com.redhat.PrinterSpooler"
  40. class PrintTestPage(Question):
  41.     STATE = { cups.IPP_JOB_PENDING: _("Pending"),
  42.               cups.IPP_JOB_HELD: _("Held"),
  43.               cups.IPP_JOB_PROCESSING: _("Processing"),
  44.               cups.IPP_JOB_STOPPED: _("Stopped"),
  45.               cups.IPP_JOB_CANCELED: _("Canceled"),
  46.               cups.IPP_JOB_ABORTED: _("Aborted"),
  47.               cups.IPP_JOB_COMPLETED: _("Completed") }
  48.  
  49.     def __init__ (self, troubleshooter):
  50.         Question.__init__ (self, troubleshooter, "Print test page")
  51.         page = gtk.VBox ()
  52.         page.set_spacing (12)
  53.         page.set_border_width (12)
  54.  
  55.         label = gtk.Label ()
  56.         label.set_alignment (0, 0)
  57.         label.set_use_markup (True)
  58.         label.set_line_wrap (True)
  59.         page.pack_start (label, False, False, 0)
  60.         self.main_label = label
  61.         self.main_label_text = ('<span weight="bold" size="larger">' +
  62.                                 _("Test Page") + '</span>\n\n' +
  63.                                 _("Now print a test page.  If you are having "
  64.                                   "problems printing a specific document, "
  65.                                   "print that document now and mark the print "
  66.                                   "job below."))
  67.  
  68.         hbox = gtk.HButtonBox ()
  69.         hbox.set_border_width (0)
  70.         hbox.set_spacing (3)
  71.         hbox.set_layout (gtk.BUTTONBOX_START)
  72.         self.print_button = gtk.Button (_("Print Test Page"))
  73.         hbox.pack_start (self.print_button, False, False, 0)
  74.  
  75.         self.cancel_button = gtk.Button (_("Cancel All Jobs"))
  76.         hbox.pack_start (self.cancel_button, False, False, 0)
  77.         page.pack_start (hbox, False, False, 0)
  78.  
  79.         tv = gtk.TreeView ()
  80.         test_cell = gtk.CellRendererToggle ()
  81.         test = gtk.TreeViewColumn (_("Test"), test_cell, active=0)
  82.         job = gtk.TreeViewColumn (_("Job"), gtk.CellRendererText (), text=1)
  83.         printer_cell = gtk.CellRendererText ()
  84.         printer = gtk.TreeViewColumn (_("Printer"), printer_cell, text=2)
  85.         name_cell = gtk.CellRendererText ()
  86.         name = gtk.TreeViewColumn (_("Document"), name_cell, text=3)
  87.         status = gtk.TreeViewColumn (_("Status"), gtk.CellRendererText (),
  88.                                      text=4)
  89.         test_cell.set_radio (False)
  90.         self.test_cell = test_cell
  91.         printer.set_resizable (True)
  92.         printer_cell.set_property ("ellipsize", pango.ELLIPSIZE_END)
  93.         printer_cell.set_property ("width-chars", 20)
  94.         name.set_resizable (True)
  95.         name_cell.set_property ("ellipsize", pango.ELLIPSIZE_END)
  96.         name_cell.set_property ("width-chars", 20)
  97.         status.set_resizable (True)
  98.         tv.append_column (test)
  99.         tv.append_column (job)
  100.         tv.append_column (printer)
  101.         tv.append_column (name)
  102.         tv.append_column (status)
  103.         tv.set_rules_hint (True)
  104.         sw = gtk.ScrolledWindow ()
  105.         sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  106.         sw.set_shadow_type (gtk.SHADOW_IN)
  107.         sw.add (tv)
  108.         self.treeview = tv
  109.         page.pack_start (sw)
  110.  
  111.         label = gtk.Label (_("Did the marked print jobs print correctly?"))
  112.         label.set_line_wrap (True)
  113.         label.set_alignment (0, 0)
  114.         page.pack_start (label, False, False, 0)
  115.  
  116.         vbox = gtk.VBox ()
  117.         vbox.set_spacing (6)
  118.         self.yes = gtk.RadioButton (label=_("Yes"))
  119.         no = gtk.RadioButton (label=_("No"))
  120.         no.set_group (self.yes)
  121.         vbox.pack_start (self.yes, False, False, 0)
  122.         vbox.pack_start (no, False, False, 0)
  123.         page.pack_start (vbox, False, False, 0)
  124.         self.persistent_answers = {}
  125.         troubleshooter.new_page (page, self)
  126.  
  127.     def display (self):
  128.         answers = self.troubleshooter.answers
  129.         if not answers.has_key ('cups_queue'):
  130.             return False
  131.  
  132.         parent = self.troubleshooter.get_window ()
  133.         self.authconn = answers['_authenticated_connection']
  134.         mediatype = None
  135.         defaults = answers.get ('cups_printer_ppd_defaults', {})
  136.         for opts in defaults.values ():
  137.             for opt, value in opts.iteritems ():
  138.                 if opt == "MediaType":
  139.                     mediatype = value
  140.                     break
  141.  
  142.         if mediatype != None:
  143.             mediatype_string = '\n\n' + (_("Remember to load paper of type "
  144.                                            "'%s' into the printer first.") %
  145.                                          mediatype)
  146.         else:
  147.             mediatype_string = ""
  148.  
  149.         label_text = self.main_label_text + mediatype_string
  150.         self.main_label.set_markup (label_text)
  151.  
  152.         model = gtk.ListStore (gobject.TYPE_BOOLEAN,
  153.                                gobject.TYPE_INT,
  154.                                gobject.TYPE_STRING,
  155.                                gobject.TYPE_STRING,
  156.                                gobject.TYPE_STRING)
  157.         self.treeview.set_model (model)
  158.         self.job_to_iter = {}
  159.  
  160.         test_jobs = self.persistent_answers.get ('test_page_job_id', [])
  161.         def get_jobs ():
  162.             c = self.authconn
  163.             jobs_dict = c.getJobs (which_jobs='not-completed',
  164.                                    my_jobs=False)
  165.             completed_jobs_dict = c.getJobs (which_jobs='completed')
  166.             return (jobs_dict, completed_jobs_dict)
  167.  
  168.         self.op = TimedOperation (get_jobs, parent=parent)
  169.         (jobs_dict, completed_jobs_dict) = self.op.run ()
  170.  
  171.         # We want to display the jobs in the queue for this printer...
  172.         try:
  173.             queue_uri_ending = "/" + self.troubleshooter.answers['cups_queue']
  174.             jobs_on_this_printer = filter (lambda x:
  175.                                                jobs_dict[x]['job-printer-uri'].\
  176.                                                endswith (queue_uri_ending),
  177.                                            jobs_dict.keys ())
  178.         except:
  179.             jobs_on_this_printer = []
  180.  
  181.         # ...as well as any other jobs we've previous submitted as test pages.
  182.         jobs = list (set(test_jobs).union (set (jobs_on_this_printer)))
  183.  
  184.         completed_jobs_dict = None
  185.         for job in jobs:
  186.             try:
  187.                 j = jobs_dict[job]
  188.             except KeyError:
  189.                 try:
  190.                     j = completed_jobs_dict[job]
  191.                 except KeyError:
  192.                     continue
  193.  
  194.             iter = model.append (None)
  195.             self.job_to_iter[job] = iter
  196.             model.set_value (iter, 0, job in test_jobs)
  197.             model.set_value (iter, 1, job)
  198.             self.update_job (job, j)
  199.  
  200.         return True
  201.  
  202.     def connect_signals (self, handler):
  203.         self.print_sigid = self.print_button.connect ("clicked",
  204.                                                       self.print_clicked)
  205.         self.cancel_sigid = self.cancel_button.connect ("clicked",
  206.                                                         self.cancel_clicked)
  207.         self.test_sigid = self.test_cell.connect ('toggled',
  208.                                                   self.test_toggled)
  209.  
  210.         def create_subscription ():
  211.             c = self.authconn
  212.             sub_id = c.createSubscription ("/",
  213.                                            events=["job-created",
  214.                                                    "job-completed",
  215.                                                    "job-stopped",
  216.                                                    "job-progress",
  217.                                                    "job-state-changed"])
  218.             return sub_id
  219.  
  220.         parent = self.troubleshooter.get_window ()
  221.         self.op = TimedOperation (create_subscription, parent=parent)
  222.         self.sub_id = self.op.run ()
  223.  
  224.         try:
  225.             bus = dbus.SystemBus ()
  226.         except:
  227.             bus = None
  228.  
  229.         self.bus = bus
  230.         if bus:
  231.             bus.add_signal_receiver (self.handle_dbus_signal,
  232.                                      path=DBUS_PATH,
  233.                                      dbus_interface=DBUS_IFACE)
  234.  
  235.         self.timer = gobject.timeout_add (1000, self.update_jobs_list)
  236.  
  237.     def disconnect_signals (self):
  238.         if self.bus:
  239.             self.bus.remove_signal_receiver (self.handle_dbus_signal,
  240.                                              path=DBUS_PATH,
  241.                                              dbus_interface=DBUS_IFACE)
  242.                                              
  243.         self.print_button.disconnect (self.print_sigid)
  244.         self.cancel_button.disconnect (self.cancel_sigid)
  245.         self.test_cell.disconnect (self.test_sigid)
  246.  
  247.         def cancel_subscription (sub_id):
  248.             c = self.authconn
  249.             c.cancelSubscription (sub_id)
  250.  
  251.         parent = self.troubleshooter.get_window ()
  252.         self.op = TimedOperation (cancel_subscription,
  253.                                   (self.sub_id,),
  254.                                   parent=parent)
  255.         self.op.run ()
  256.         try:
  257.             del self.sub_seq
  258.         except:
  259.             pass
  260.  
  261.         gobject.source_remove (self.timer)
  262.  
  263.     def collect_answer (self):
  264.         if not self.displayed:
  265.             return {}
  266.  
  267.         self.answers = self.persistent_answers.copy ()
  268.         parent = self.troubleshooter.get_window ()
  269.         success = self.yes.get_active ()
  270.         self.answers['test_page_successful'] = success
  271.  
  272.         class collect_jobs:
  273.             def __init__ (self, model):
  274.                 self.jobs = []
  275.                 model.foreach (self.each, None)
  276.  
  277.             def each (self, model, path, iter, user_data):
  278.                 self.jobs.append (model.get (iter, 0, 1, 2, 3, 4))
  279.  
  280.         model = self.treeview.get_model ()
  281.         jobs = collect_jobs (model).jobs
  282.         def collect_attributes (jobs):
  283.             job_attrs = None
  284.             c = self.authconn
  285.             with_attrs = []
  286.             for (test, jobid, printer, doc, status) in jobs:
  287.                 attrs = None
  288.                 if test:
  289.                     try:
  290.                         attrs = c.getJobAttributes (jobid)
  291.                     except AttributeError:
  292.                         # getJobAttributes was introduced in pycups 1.9.35.
  293.                         if job_attrs == None:
  294.                             job_attrs = c.getJobs (which_jobs='all')
  295.  
  296.                         attrs = self.job_attrs[jobid]
  297.  
  298.                 with_attrs.append ((test, jobid, printer, doc, status, attrs))
  299.  
  300.             return with_attrs
  301.  
  302.         self.op = TimedOperation (collect_attributes,
  303.                                   (jobs,),
  304.                                   parent=parent)
  305.         with_attrs = self.op.run ()
  306.         self.answers['test_page_job_status'] = with_attrs
  307.         return self.answers
  308.  
  309.     def cancel_operation (self):
  310.         self.op.cancel ()
  311.  
  312.         # Abandon the CUPS connection and make another.
  313.         answers = self.troubleshooter.answers
  314.         factory = answers['_authenticated_connection_factory']
  315.         self.authconn = factory.get_connection ()
  316.         self.answers['_authenticated_connection'] = self.authconn
  317.  
  318.     def handle_dbus_signal (self, *args):
  319.         debugprint ("D-Bus signal caught: updating jobs list soon")
  320.         gobject.source_remove (self.timer)
  321.         self.timer = gobject.timeout_add (200, self.update_jobs_list)
  322.  
  323.     def update_job (self, jobid, job_dict):
  324.         iter = self.job_to_iter[jobid]
  325.         model = self.treeview.get_model ()
  326.         try:
  327.             printer_name = job_dict['printer-name']
  328.         except KeyError:
  329.             try:
  330.                 uri = job_dict['job-printer-uri']
  331.                 r = uri.rfind ('/')
  332.                 printer_name = uri[r + 1:]
  333.             except KeyError:
  334.                 printer_name = None
  335.  
  336.         if printer_name != None:
  337.             model.set_value (iter, 2, printer_name)
  338.  
  339.         model.set_value (iter, 3, job_dict['job-name'])
  340.         model.set_value (iter, 4, self.STATE[job_dict['job-state']])
  341.  
  342.     def print_clicked (self, widget):
  343.         now = time.time ()
  344.         tt = time.localtime (now)
  345.         when = time.strftime ("%d/%b/%Y:%T %z", tt)
  346.         self.persistent_answers['test_page_attempted'] = when
  347.         answers = self.troubleshooter.answers
  348.         parent = self.troubleshooter.get_window ()
  349.  
  350.         def print_test_page (*args, **kwargs):
  351.             factory = answers['_authenticated_connection_factory']
  352.             c = factory.get_connection ()
  353.             return c.printTestPage (*args, **kwargs)
  354.  
  355.         tmpfname = None
  356.         mimetypes = [None, 'text/plain']
  357.         for mimetype in mimetypes:
  358.             try:
  359.                 if mimetype == None:
  360.                     # Default test page.
  361.                     self.op = TimedOperation (print_test_page,
  362.                                               (answers['cups_queue'],),
  363.                                               parent=parent)
  364.                     jobid = self.op.run ()
  365.                 elif mimetype == 'text/plain':
  366.                     (tmpfd, tmpfname) = tempfile.mkstemp ()
  367.                     os.write (tmpfd, "This is a test page.\n")
  368.                     os.close (tmpfd)
  369.                     self.op = TimedOperation (print_test_page,
  370.                                               (answers['cups_queue'],),
  371.                                               kwargs={'file': tmpfname,
  372.                                                       'format': mimetype},
  373.                                               parent=parent)
  374.                     jobid = self.op.run ()
  375.                     try:
  376.                         os.unlink (tmpfname)
  377.                     except OSError:
  378.                         pass
  379.  
  380.                     tmpfname = None
  381.  
  382.                 jobs = self.persistent_answers.get ('test_page_job_id', [])
  383.                 jobs.append (jobid)
  384.                 self.persistent_answers['test_page_job_id'] = jobs
  385.                 break
  386.             except RuntimeError:
  387.                 self.persistent_answers['test_page_submit_failure'] = 'connect'
  388.                 break
  389.             except cups.IPPError, (e, s):
  390.                 if (e == cups.IPP_DOCUMENT_FORMAT and
  391.                     mimetypes.index (mimetype) < (len (mimetypes) - 1)):
  392.                     # Try next format.
  393.                     if tmpfname != None:
  394.                         os.unlink (tmpfname)
  395.                         tmpfname = None
  396.                     continue
  397.  
  398.                 self.persistent_answers['test_page_submit_failure'] = (e, s)
  399.                 show_error_dialog (_("Error submitting test page"),
  400.                                    _("There was an error during the CUPS "
  401.                                      "operation: '%s'.") % s,
  402.                                    self.troubleshooter.get_window ())
  403.                 break
  404.  
  405.     def cancel_clicked (self, widget):
  406.         self.persistent_answers['test_page_jobs_cancelled'] = True
  407.         jobids = []
  408.         for jobid, iter in self.job_to_iter.iteritems ():
  409.             jobids.append (jobid)
  410.  
  411.         def cancel_jobs (jobids):
  412.             c = self.authconn
  413.             for jobid in jobids:
  414.                 try:
  415.                     c.cancelJob (jobid)
  416.                 except cups.IPPError, (e, s):
  417.                     if e != cups.IPP_NOT_POSSIBLE:
  418.                         self.persistent_answers['test_page_cancel_failure'] = (e, s)
  419.  
  420.         self.op = TimedOperation (cancel_jobs,
  421.                                   (jobids,),
  422.                                   parent=self.troubleshooter.get_window ())
  423.         self.op.run ()
  424.  
  425.     def test_toggled (self, cell, path):
  426.         model = self.treeview.get_model ()
  427.         iter = model.get_iter (path)
  428.         active = model.get_value (iter, 0)
  429.         model.set_value (iter, 0, not active)
  430.  
  431.     def update_jobs_list (self):
  432.         def get_notifications (self):
  433.             c = self.authconn
  434.             try:
  435.                 notifications = c.getNotifications ([self.sub_id],
  436.                                                     [self.sub_seq + 1])
  437.             except AttributeError:
  438.                 notifications = c.getNotifications ([self.sub_id])
  439.  
  440.             return notifications
  441.  
  442.         # Enter the GDK lock.  We need to do this because we were
  443.         # called from a timeout.
  444.         gtk.gdk.threads_enter ()
  445.  
  446.         parent = self.troubleshooter.get_window ()
  447.         self.op = TimedOperation (get_notifications,
  448.                                   (self,),
  449.                                   parent=parent)
  450.         notifications = self.op.run ()
  451.  
  452.         answers = self.troubleshooter.answers
  453.         model = self.treeview.get_model ()
  454.         queue = answers['cups_queue']
  455.         test_jobs = self.persistent_answers.get('test_page_job_id', [])
  456.         for event in notifications['events']:
  457.             seq = event['notify-sequence-number']
  458.             try:
  459.                 if seq <= self.sub_seq:
  460.                     # Work around a bug in pycups < 1.9.34
  461.                     continue
  462.             except AttributeError:
  463.                 pass
  464.             self.sub_seq = seq
  465.             job = event['notify-job-id']
  466.  
  467.             nse = event['notify-subscribed-event']
  468.             if nse == 'job-created':
  469.                 if (job in test_jobs or
  470.                     event['printer-name'] == queue):
  471.                     iter = model.append (None)
  472.                     self.job_to_iter[job] = iter
  473.                     model.set_value (iter, 0, True)
  474.                     model.set_value (iter, 1, job)
  475.                 else:
  476.                     continue
  477.             elif not self.job_to_iter.has_key (job):
  478.                 continue
  479.  
  480.             if (job in test_jobs and
  481.                 nse in ["job-stopped", "job-completed"]):
  482.                 comp = self.persistent_answers.get ('test_page_completions', [])
  483.                 comp.append ((job, event['notify-text']))
  484.                 self.persistent_answers['test_page_completions'] = comp
  485.  
  486.             self.update_job (job, event)
  487.  
  488.         # Update again when we're told to. (But we might update sooner if
  489.         # there is a D-Bus signal.)
  490.         gobject.source_remove (self.timer)
  491.         self.timer = gobject.timeout_add (1000 *
  492.                                           notifications['notify-get-interval'],
  493.                                           self.update_jobs_list)
  494.         debugprint ("Update again in %ds" %
  495.                     notifications['notify-get-interval'])
  496.         gtk.gdk.threads_leave ()
  497.         return False
  498.